package top.shmly.universal.config.security;

import com.alibaba.fastjson.JSONObject;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import top.shmly.universal.base.constant.JWTConstants;
import top.shmly.universal.base.response.IResponseResult;
import top.shmly.universal.base.response.ResponseCode;
import top.shmly.universal.base.response.ResponseResult;
import top.shmly.universal.utils.Md5Utils;
import top.shmly.universal.utils.impl.Md5UtilsImpl;
import top.shmly.universal.utils.impl.ResponseJsonUtilsImpl;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @program: universal
 * @description:
 * @author: Jibny
 * @date 2022-01-21 14:22:58
 */
@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    /**
     * 过期时间
     */
    private static final long EXPIRE_TIME = JWTConstants.EXPIRATION;

    private AuthenticationManager authenticationManager;

    private RedisTemplate redisTemplate;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate) {
        this.authenticationManager = authenticationManager;
        this.redisTemplate = redisTemplate;
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }

    @SneakyThrows
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        AuthUser user = (AuthUser) authResult.getPrincipal();
        /**
         * 1、创建密钥
         */
        MACSigner macSigner = new MACSigner(JWTConstants.SECRET);
        /**
         * 2、payload
         */
        String payload = JSONObject.toJSONString(user);
        JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
                .subject("subject")
                .claim("payload", payload)
                .expirationTime(new Date(System.currentTimeMillis() + EXPIRE_TIME))
                .build();
        JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
        /**
         * 创建签名的JWT
         */
        SignedJWT signedJWT = new SignedJWT(jwsHeader, claimsSet);
        signedJWT.sign(macSigner);
        /**
         * 生成token
         */
        String jwtToken = signedJWT.serialize();
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        //生成Key， 把权限放入到redis中
        String keyPrefix = "JWT" + user.getUsername() + ":";
        String keySuffix = Md5UtilsImpl.getInstance().getMD5(jwtToken.getBytes());
        String key = keyPrefix + keySuffix;

        String authKey = key + ":Authorities";

        redisTemplate.opsForValue().set(key, jwtToken, EXPIRE_TIME, TimeUnit.MILLISECONDS);

        redisTemplate.opsForValue().set(authKey, JSONObject.toJSONString(user.getAuthorities()), EXPIRE_TIME, TimeUnit.SECONDS);

        ResponseJsonUtilsImpl.getInstance()
                .sendMsgWithServletResponse(response, HttpServletResponse.SC_OK, new ResponseResult<>().ok(JWTConstants.TOKEN_PREFIX.concat(jwtToken)));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        log.error("登录认证失败", failed);
        String result = "";
        if (failed instanceof UsernameNotFoundException) {
            result = "用户不存在";
        } else if (failed instanceof BadCredentialsException) {
            result = "用户名密码错误";
        }
        ResponseJsonUtilsImpl.getInstance()
                .sendMsgWithServletResponse(response, HttpServletResponse.SC_OK, new ResponseResult<>().ok(ResponseCode.CODE_401, result));
    }
}
